package com.li.springbootproject.config.requestlimit.counterlimit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalTime;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 固定窗口计数器限流
 *
 * @author LiDong
 * @version 1.0.0
 * @createTime 2021/12/26 10:21
 */
public class FixedWindowCounterLimiting extends CounterLimit {

    private static final Logger logger = LoggerFactory.getLogger(CounterLimit.class);

    /**
     * 计数器
     */
    private final AtomicInteger counter = new AtomicInteger();

    public FixedWindowCounterLimiting(long limitTime, int limitCount, TimeUnit timeUnit) {
        this.limitTime = limitTime;
        this.limitCount = limitCount;
        this.timeUnit = timeUnit;
        // 开启一个线程维护计数器当到达限流时间的时候重置计数器
        new Thread(new CounterResetThread()).start();
    }

    /**
     * 计数器加 1 返回 true 能够正常访问接口，false 表示访问受限
     *
     * @return
     */
    @Override
    public boolean count() {
        while (true) {
            // 当前受限状态
            if (limited) {
                return false;
            } else {
                int currentCount = counter.get();
                // 计数器达到限流的状态
                if (currentCount == limitCount) {
                    logger.info("限流：{}", LocalTime.now().toString());
                    limited = true;
                    return false;
                } else {
                    if (counter.compareAndSet(currentCount, currentCount + 1)) {
                        return true;
                    }
                }
            }
        }
    }

    private class CounterResetThread implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    timeUnit.sleep(limitTime);
                    // 计数器清 0
                    counter.compareAndSet(limitCount, 0);
                    // logger.info("=====> 计数器重置......");
                    // 修改当前请求状态为不受限
                    limited = false;
                } catch (Exception e) {
                    logger.error("Error Occur:{0}", e);
                }
            }

        }
    }
}
